package org.summerframework.component.security.session.config;

import org.summerframework.component.security.session.resolver.PlatFormCookieSerializer;
import org.summerframework.component.security.session.resolver.WebSessionIdResolver;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.web.servlet.server.Session;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 石超
 * @version v1.0.0
 */
@Configuration
@EnableConfigurationProperties({SessionIdAutoConfiguration.Cookie.class, ServerProperties.class, SessionIdAutoConfiguration.HeaderHttpSessionIdProperty.class, SessionIdAutoConfiguration.UrlHttpSessionIdProperty.class})
@AutoConfigureBefore({HttpSessionConfig.class, SessionAutoConfiguration.class,})
public class SessionIdAutoConfiguration {
    private static final String REMEMBER_ME_SERVICES_CLASS = "org.springframework.security.web.authentication.RememberMeServices";

    @Bean
    @ConditionalOnMissingBean(name = "webSessionIdResolver")
    public WebSessionIdResolver webSessionIdResolver() {
        return new WebSessionIdResolver();
    }

    @Bean
    @ConditionalOnMissingBean(name = "cookieSerializer")
    public CookieSerializer cookieSerializer(Cookie cookie) {
        PlatFormCookieSerializer cookieSerializer = new PlatFormCookieSerializer();

        PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
        map.from(cookie::getName).to(cookieSerializer::setCookieName);
        map.from(cookie::getDomain).to(cookieSerializer::setDomainName);
        map.from(cookie::getPath).to(cookieSerializer::setCookiePath);
        map.from(cookie::getHttpOnly).to(cookieSerializer::setUseHttpOnlyCookie);
        map.from(cookie::getSecure).to(cookieSerializer::setUseSecureCookie);
        map.from(cookie::getNames).to(cookieSerializer::setCookieNames);
        map.from(cookie::getMaxAge).to((maxAge) -> cookieSerializer.setCookieMaxAge((int) maxAge.getSeconds()));

        if (StringUtils.isEmpty(cookie.getSameSite())) {
            cookieSerializer.setSameSite(null);
        } else {
            cookieSerializer.setSameSite(cookie.getSameSite());
        }


        if (ClassUtils.isPresent(REMEMBER_ME_SERVICES_CLASS, getClass().getClassLoader())) {
            new RememberMeServicesCookieSerializerCustomizer().apply(cookieSerializer);
        }
        return cookieSerializer;
    }

    @Getter
    @Setter
    @ConfigurationProperties(prefix = "server.servlet.session.cookie")
    public static class Cookie extends Session.Cookie {
        private String sameSite;

        @NestedConfigurationProperty
        private Map<String, String> names = new HashMap<>();
    }

    @Getter
    @Setter
    @ConfigurationProperties(prefix = "server.servlet.session.head")
    public static class HeaderHttpSessionIdProperty {
        private String name;
    }

    @Getter
    @Setter
    @ConfigurationProperties(prefix = "server.servlet.session.url")
    public static class UrlHttpSessionIdProperty {
        private String name;
    }

    /**
     * Customization for {@link SpringSessionRememberMeServices} that is only instantiated
     * when Spring Security is on the classpath.
     */
    static class RememberMeServicesCookieSerializerCustomizer {

        void apply(PlatFormCookieSerializer cookieSerializer) {
            cookieSerializer.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
        }

    }
}
